using System;
using System.Runtime.InteropServices;

namespace Tamir.IPLib
{
	/// <summary>
	/// Summary description for IPHelper.
	/// </summary>
	public class IPHelper
	{
		private const	int		MAX_ADAPTER_NAME				= 128;
		private const	int		MAX_ADAPTER_NAME_LENGTH			= 256;
		private const	int		MAX_ADAPTER_DESCRIPTION_LENGTH	= 128;
		private const	int		MAX_ADAPTER_ADDRESS_LENGTH		= 8;
		private const	int		ERROR_OK						= 0;
		private static	uint	ERROR_BUFFER_OVERFLOW			= (uint)111;

		#region Structs
			
		/// <summary>
		/// Represent a row in an INTF_TABLE (Interfaces Table)
		/// </summary>
		[ComVisible(false),StructLayout(LayoutKind.Sequential,CharSet=CharSet.Unicode)]
			internal struct INTF_ROW
		{
			[MarshalAs(UnmanagedType.ByValTStr,SizeConst=256)]
			internal String		Name;
			internal uint		Index;
			internal uint		Type;
			internal uint		Mtu;
			internal uint		Speed;
			internal uint		PhysAddrLen;//MAC Address length

			[MarshalAs(UnmanagedType.ByValArray,SizeConst=MAX_ADAPTER_ADDRESS_LENGTH)]
			internal byte[]     PhysAddr;//The MAC address
			internal uint		AdminStatus;
			internal uint		OperStatus;
			internal uint		LastChange;
			internal uint		InOctets;
			internal uint		dwInUcastPkts ;
			internal uint		dwInNUcastPkts ;
			internal uint		dwInDiscards;
			internal uint		dwInErrors ;
			internal uint		dwInUnknownProtos ;
			internal uint		dwOutOctets;
			internal uint		dwOutUcastPkts;
			internal uint		dwOutNUcastPkts ;
			internal uint		dwOutDiscards;
			internal uint		dwOutErrors ;
			internal uint		dwOutQLen;
			internal uint		dwDescrLen ;

			[MarshalAs(UnmanagedType.ByValArray,SizeConst=256)]
			internal byte[]     bDescr;//The description of this interface
		}

		[ComVisible(false),StructLayout(LayoutKind.Sequential,CharSet=CharSet.Unicode)]
			internal struct IP_ADAPTER_INDEX_MAP
		{
			internal uint		Index;
			[MarshalAs(UnmanagedType.ByValTStr,SizeConst=128)]
			internal String		Name;
		}


		[ComVisible(false),StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi)]
			internal struct IP_ADAPTER_INFO 
		{
			internal int /* struct _IP_ADAPTER_INFO* */ Next;
			internal uint           ComboIndex;

			[MarshalAs(UnmanagedType.ByValTStr,SizeConst=MAX_ADAPTER_NAME_LENGTH + 4)]
			internal String         AdapterName;

			[MarshalAs(UnmanagedType.ByValTStr,SizeConst=MAX_ADAPTER_DESCRIPTION_LENGTH + 4)]
			internal String         Description;
			internal int           AddressLength;

			[MarshalAs(UnmanagedType.ByValArray,SizeConst=MAX_ADAPTER_ADDRESS_LENGTH)]
			internal byte[]         Address;
			internal int           Index;
			internal int           Type;
			internal int           DhcpEnabled;
			public uint           CurrentIpAddress; /* IP_ADDR_STRING* */
			internal IP_ADDR_STRING IpAddressList;
			internal IP_ADDR_STRING GatewayList;
			internal IP_ADDR_STRING DhcpServer;
			[MarshalAs(UnmanagedType.Bool)]
			internal bool           HaveWins;
			internal IP_ADDR_STRING PrimaryWinsServer;
			internal IP_ADDR_STRING SecondaryWinsServer;
			internal uint/*time_t*/ LeaseObtained;
			internal uint/*time_t*/ LeaseExpires;
		};

		/// <summary>
		/// MAC_ADDR_ARR struct for "iphlpapi.dll" invocation.
		/// </summary>
		[ComVisible(false),StructLayout(LayoutKind.Sequential)]
			internal struct MAC_ADDR_ARRAY 
		{
			[MarshalAs(UnmanagedType.ByValArray,SizeConst=6)]
			public byte[] bytes;
		};

		/// <summary>
		/// Represent a row in an IP address
		/// </summary>
		[ComVisible(false),StructLayout(LayoutKind.Sequential)]
			internal struct IPADDR
		{
			internal byte s_b1;
			internal byte s_b2;
			internal byte s_b3;
			internal byte s_b4;
		};

		/// <summary>
		/// Represent a row in an IP mask
		/// </summary>
		[ComVisible(false),StructLayout(LayoutKind.Sequential)]
			internal struct IPMASK
		{
			internal byte s_b1;
			internal byte s_b2;
			internal byte s_b3;
			internal byte s_b4;
		};

		[ComVisible(false),StructLayout(LayoutKind.Sequential)]
			internal struct  IPFORWARDROW 
		{
			internal int /*DWORD*/ dwForwardDest;  
			internal int /*DWORD*/ dwForwardMask;  
			internal int /*DWORD*/ dwForwardPolicy;  
			internal int /*DWORD*/ dwForwardNextHop;  
			internal int /*DWORD*/ dwForwardIfIndex;  
			internal int /*DWORD*/ dwForwardType;  
			internal int /*DWORD*/ dwForwardProto;  
			internal int /*DWORD*/ dwForwardAge;  
			internal int /*DWORD*/ dwForwardNextHopAS;  
			internal int /*DWORD*/ dwForwardMetric1;  
			internal int /*DWORD*/ dwForwardMetric2;  
			internal int /*DWORD*/ dwForwardMetric3;  
			internal int /*DWORD*/ dwForwardMetric4;  
			internal int /*DWORD*/ dwForwardMetric5;
		};

		[ComVisible(false),StructLayout(LayoutKind.Sequential)]
			internal struct IPFORWARDTABLE 
		{
			uint /*DWORD*/						dwNumEntries;  
			uint /*struct MIB_IPFORWARDROW*/	table;
		};

		/// <summary>
		/// IP_ADDRESS_STRING struct for "iphlpapi.dll" invocation.
		/// </summary>
		[ComVisible(false),StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi)]
			internal struct IP_ADDRESS_STRING 
		{
			[MarshalAs(UnmanagedType.ByValTStr,SizeConst=16)]
			public string address;
		};

		/// <summary>
		/// IP_MASK_STRING struct for "iphlpapi.dll" invocation.
		/// </summary>
		[ComVisible(false),StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi)]
			internal struct IP_MASK_STRING 
		{
			[MarshalAs(UnmanagedType.ByValTStr,SizeConst=16)]
			public string address;
		};

		/// <summary>
		/// IP_ADDR_STRING struct for "iphlpapi.dll" invocation.
		/// </summary>
		[ComVisible(false),StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi)]
			internal struct IP_ADDR_STRING 
		{
			public int Next;      /* struct _IP_ADDR_STRING* */
			public IP_ADDRESS_STRING IpAddress;
			public IP_MASK_STRING IpMask;
			public uint Context;
		};


		#endregion Structs

		#region iphlpapi.dll external function

		[DllImport("iphlpapi", CharSet=CharSet.Auto)]
		private extern static uint GetAdaptersInfo(IntPtr pAdapterInfo,ref int pOutBufLen);

		[DllImport("iphlpapi", CharSet=CharSet.Auto)]
		private extern static int GetInterfaceInfo(IntPtr pIfTable,ref	int pOutBufLen);

		[DllImport("iphlpapi", CharSet=CharSet.Auto)]
		private extern static uint IpReleaseAddress(IntPtr AdapterInfo);

		[DllImport("iphlpapi", CharSet=CharSet.Auto)]
		private extern static uint IpRenewAddress(IntPtr AdapterInfo);

		[DllImport("iphlpapi", CharSet=CharSet.Auto)]
		private extern static int GetIfTable(IntPtr pIfTable,ref int pdwSize,bool bOrder);

		[DllImport("iphlpapi", CharSet=CharSet.Auto)]
		private extern static int SendARP(int DestIP,int SrcIP,IntPtr pMacAddr,ref int PhyAddrLen);

		[DllImport("iphlpapi", CharSet=CharSet.Auto)]
		private extern static int AddIPAddress(IPADDR Address, IPMASK IpMask,	uint IfIndex, out ulong NTEContext, out ulong NTEInstance);

		[DllImport("iphlpapi", CharSet=CharSet.Auto)]
		private extern static int DeleteIPAddress (ulong NTEContext);

		[DllImport("iphlpapi", CharSet=CharSet.Auto)]
		private extern static int SetIfEntry(IntPtr /*PMIB_IFROW*/ pIfRow);

		[DllImport("iphlpapi", CharSet=CharSet.Auto)]
		private extern static int GetIpForwardTable(IntPtr /*PMIB_IPFORWARDTABLE*/ pIpForwardTable,ref int /*PULONG*/ pdwSize,bool bOrder);

		[DllImport("iphlpapi", CharSet=CharSet.Auto)]
		private extern static int DeleteIpForwardEntry(IntPtr /*PMIB_IPFORWARDROW*/ pRoute);

		[DllImport("iphlpapi", CharSet=CharSet.Auto)]
		private extern static int CreateIpForwardEntry(IntPtr /*PMIB_IPFORWARDROW*/ pRoute);

		#endregion iphlpapi.dll external function

		/// <summary>
		/// Returns an IP_ADAPTER_INFO struct that represents a network adapter on this machine
		/// </summary>
		internal static IP_ADAPTER_INFO GetAdapterInfo(string adapterName) 
		{
			int size = Marshal.SizeOf(typeof(IP_ADAPTER_INFO));
			IntPtr buffer = Marshal.AllocHGlobal(size);
			uint result = GetAdaptersInfo(buffer,ref size);

			if (result == ERROR_BUFFER_OVERFLOW) 
			{				
				Marshal.FreeHGlobal(buffer);
				buffer = Marshal.AllocHGlobal(size);
				result = GetAdaptersInfo(buffer,ref size);
			}

			if (result == ERROR_OK) 
			{				
				int next=(int)buffer;
				IP_ADAPTER_INFO info;
				while (next != 0) 
				{
					info = (IP_ADAPTER_INFO)Marshal.PtrToStructure((IntPtr)next,typeof(IP_ADAPTER_INFO));
					next=info.Next;
					if(info.AdapterName==adapterName)
					{
						Marshal.FreeHGlobal(buffer);
						return info;
					}
				}
				Marshal.FreeHGlobal(buffer);
				throw new IPHelper_DeviceDoesntExistsException("GetAdaptersInfo failed: adapter doesn't exists: " +
					adapterName);
			} 
			else 
			{
				Marshal.FreeHGlobal(buffer);
				throw new InvalidOperationException("GetAdaptersInfo failed: " +
					result);
			}
		}

		/// <summary>
		/// Return all network devices available on this machine through the IPHelper API
		/// </summary>
		public static NetworkDeviceList GetAllDevices()
		{
			int size = Marshal.SizeOf(typeof(IP_ADAPTER_INFO));
			IntPtr buffer = Marshal.AllocHGlobal(size);
			uint result = GetAdaptersInfo(buffer,ref size);

			NetworkDeviceList deviceList = new NetworkDeviceList();

			if (result == ERROR_BUFFER_OVERFLOW) 
			{				
				Marshal.FreeHGlobal(buffer);
				buffer = Marshal.AllocHGlobal(size);
				result = GetAdaptersInfo(buffer,ref size);
			}

			if (result == ERROR_OK) 
			{				
				int next=(int)buffer;
				IP_ADAPTER_INFO info;
				while (next != 0) 
				{
					info = (IP_ADAPTER_INFO)Marshal.PtrToStructure((IntPtr)next,typeof(IP_ADAPTER_INFO));
					next=info.Next;
					deviceList.Add( new NetworkDevice(info) );
				}
				return deviceList;
			} 
			else 
			{
				Marshal.FreeHGlobal(buffer);
				throw new InvalidOperationException("GetAdaptersInfo failed: " +
					result);
			}
		}
	}

	public class IPHelper_DeviceDoesntExistsException : Exception 
	{
		public IPHelper_DeviceDoesntExistsException(string msg):base(msg)
		{
		}
	}
}
